/**
 * Copyright 2017 orivil Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

// service 包是 golang 版的对象容器(也叫服务容器), 可轻松实现对象依赖的注入
package service

import "sync"

// Provider 为服务提供者, 其功能相当于 new() 方法, 用于创建对象.
type Provider func(c *Container) interface{}

// Container 作为对象容器(服务容器), 被所有服务所依赖. Container 分为单线程模式
// 及多线程模式.
type Container struct {
	instances map[*Provider]interface{}
	locker *sync.Mutex
}

// safe 为 false 时为单线程模式(即只在单线程中运行), 不需要加锁, 因此不会发生阻塞, 单线程模式
// 涵盖了大多数应用场景.
// safe 为 true 时为多线程模式, 线程安全, 但有可能因为某一个服务阻塞而造成 Container 内所
// 有服务都成为阻塞状态, 因此多线程模式下最好将不同功能的服务放在不同的容器中.
func NewContainer(safe bool) *Container {
	c := &Container{
		instances: make(map[*Provider]interface{}, 10),
	}
	if safe {
		c.locker = &sync.Mutex{}
	}
	return c
}

// Get 用于获取服务的单例对象. 无论 Get 被调用多少次, p 只执行一次.
func (c *Container) Get(p *Provider) interface{} {
	if c.locker != nil {
		c.locker.Lock()
		c.locker.Unlock()
	}
	if ins, ok := c.instances[p]; ok {
		return ins
	} else {
		ins = (*p)(c)
		c.instances[p] = ins
		return ins
	}
}

// GetNew 总是返回新的实例. 每次调用 GetNew 都会执行 p.
func (c *Container) GetNew(p *Provider) interface{} {
	return (*p)(c)
}

// 设置新的缓存并返回旧的缓存.
func (c *Container) SetCache(p *Provider, new interface{}) (old interface{}) {
	if c.locker != nil {
		c.locker.Lock()
		c.locker.Unlock()
	}
	old = c.instances[p]
	c.instances[p] = new
	return
}

// 判断 p 是否被 c.Get() 调用过, 调用过则执行 call
func (c *Container) CallIfHasCache(p *Provider, call func()) {
	c.callIfCache(p, true, call)
}

// 判断 p 是否被 c.Get() 调用过, 未调用过则执行 call
func (c *Container) CallIfNotHasCache(p *Provider, call func()) {
	c.callIfCache(p, false, call)
}

func (c *Container) callIfCache(p *Provider, has bool, call func()) {
	if c.locker != nil {
		c.locker.Lock()
		c.locker.Unlock()
	}
	if _, ok := c.instances[p]; ok == has {
		call()
	}
}